/*
 *  Copyright (C) 2010 Glauco
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.balisunrise.swing.builder;

import java.lang.reflect.Field;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JTextField;
import org.balisunrise.annotation.util.AnnotationsUtilitiesException;
import org.balisunrise.annotation.util.FieldUtilities;
import org.balisunrise.application.Application;
import org.balisunrise.builder.BuilderException;
import org.balisunrise.common.Criterionable;
import org.balisunrise.common.FieldComponent;
import org.balisunrise.common.PanelField;
import org.balisunrise.components.ComponentManager;
import org.balisunrise.swing.components.ComboBoxInput;
import org.balisunrise.swing.components.FieldLabel;
import org.balisunrise.swing.components.TextFieldInput;
import org.balisunrise.swing.components.managers.ManagerUtil;
import org.balisunrise.swing.transaction.EmbeddedPanelField;
import org.balisunrise.swing.transaction.SelectionAction;
import org.balisunrise.swing.transaction.CriterionablePanelField;
import org.balisunrise.swing.transaction.SelectionPanelField;

/**
 *
 * @author Glauco
 */
public class PanelFieldBuilder
        implements org.balisunrise.builder.PanelFieldBuilder{

    private static final String MESSAGE = "Erro ao construir uma PanelField.\n";

    public PanelField buildPanelField(Field field, String alias)
            throws BuilderException {

        if(field == null){
            throw new BuilderException(MESSAGE +
                    "field não pode ser nulo.");
        }

        if(alias == null){
            throw new BuilderException(MESSAGE +
                    "alias não pode ser nulo.");
        }

        try{
            return buildPanelField(FieldUtilities.create(field, alias));
        }catch(AnnotationsUtilitiesException auex){
            throw new BuilderException(MESSAGE +
                    auex.getMessage(), auex.getCause());
        }
    }

    public PanelField buildPanelField(FieldUtilities fieldUtilities)
            throws BuilderException {

        if(fieldUtilities == null){
            throw new BuilderException(MESSAGE +
                    "fieldUtilities não pode ser nulo.");
        }

        switch(fieldUtilities.type){
            case FieldUtilities.DEFINITION:
                return buildDefinition(fieldUtilities);
            case FieldUtilities.EMBEDDED:
                return buildEmbedded(fieldUtilities);
            case FieldUtilities.SELECTION:
                return buildSelection(fieldUtilities);
        }

        throw new BuilderException(MESSAGE +
                    "Atributo TYPE do fieldUtilities invalido.");
    }

    private PanelField buildDefinition(FieldUtilities fieldUtilities)
            throws BuilderException {

        org.balisunrise.swing.transaction.PanelField pf;

        if (fieldUtilities.selection){
            CriterionablePanelField spf = new CriterionablePanelField();
            spf.setFilterType(fieldUtilities.selectionFilterType);
            pf = spf;
        }else
            pf = new org.balisunrise.swing.transaction.PanelField();

        // cria labels
        JLabel label = new JLabel(fieldUtilities.label);
        String name = fieldUtilities.field.getDeclaringClass().getName() +
                "_panelField_label_" + fieldUtilities.field.getName() +
                "_" + fieldUtilities.alias;
        label.setName(name);
        label.getAccessibleContext().setAccessibleName(name);
        label.setFocusable(false);

        // cria FieldLabel
        FieldLabel fieldLabel = new FieldLabel(label);
        fieldLabel.setFieldClass(fieldUtilities.field.getType());
        fieldLabel.setOwner(pf);

        JLabel infoLabel = null;
        if(fieldUtilities.infoLabel.length() > 0){
            infoLabel = new JLabel(fieldUtilities.infoLabel);
            name = fieldUtilities.field.getDeclaringClass().getName() +
                    "_panelField_infoLabel_" + fieldUtilities.field.getName() +
                    "_" + fieldUtilities.alias;
            infoLabel.setName(name);
            infoLabel.getAccessibleContext().setAccessibleName(name);
            infoLabel.setFocusable(false);
        }

        // pega CM
        ComponentManager<?,JComponent> cm = Application.get()
                .getComponentManagers()
                .getComponentManager(fieldUtilities.field.getType());

        // cria componente principal
        name = fieldUtilities.field.getDeclaringClass().getName() +
                "_panelField_component_" + fieldUtilities.field.getName() +
                "_" + fieldUtilities.alias;
        JComponent component = cm.buildComponent(fieldUtilities.properties);

        ManagerUtil.setProperties(component, fieldUtilities.properties);

        FieldComponent fieldComponent = null;
        if (component instanceof JTextField){
            TextFieldInput textFieldInput = new TextFieldInput(
                    cm, (JTextField) component, !fieldUtilities.bindReadOnly);
            textFieldInput.setFieldClass(fieldUtilities.field.getType());
            fieldComponent = textFieldInput;
        }else if(component instanceof JComboBox){
            ComboBoxInput comboBoxInput = new ComboBoxInput(
                    cm, (JComboBox) component, !fieldUtilities.bindReadOnly);
            comboBoxInput.setFieldClass(fieldUtilities.field.getType());
            fieldComponent = comboBoxInput;
        }
        fieldComponent.setOwner(pf);

        // monta lista dos componentes
        pf.getFields().add(fieldLabel);
        pf.getFields().add(fieldComponent);
        if (infoLabel != null){
            FieldLabel infoFieldLabel = new FieldLabel(infoLabel);
            infoFieldLabel.setFieldClass(fieldUtilities.field.getType());
            infoFieldLabel.setOwner(pf);
            pf.getFields().add(infoFieldLabel);
        }

        name = fieldUtilities.field.getDeclaringClass().getName() +
                "_panelField_" + fieldUtilities.field.getName() +
                "_" + fieldUtilities.alias;

        // atribui dados
        pf.setName(name);
        pf.setJumpToNextRow(fieldUtilities.jumpToNextRow);
        pf.setFieldClass(fieldUtilities.field.getType());
        pf.setFieldName(fieldUtilities.field.getName());

        return pf;
    }

    private PanelField buildEmbedded(FieldUtilities fieldUtilities)
            throws BuilderException {

        // grupo
        EmbeddedPanelField group = new EmbeddedPanelField();

        String name = fieldUtilities.field.getDeclaringClass().getName() +
                "_embeddedPanelGroup_" + fieldUtilities.field.getName() +
                "_" + fieldUtilities.alias;

        // atribui os dados do grupo
        group.setName(name);
        group.setFieldClass(fieldUtilities.field.getType());
        group.setFieldName(fieldUtilities.field.getName());

        // para cada campo emcapsulado
        for (FieldUtilities fu : fieldUtilities.embedded) {
            // cria novos campos
            PanelField newPF = buildPanelField(fu);
            newPF.setOwner(group);

            if(newPF instanceof Criterionable)
                ((Criterionable)newPF).addPrefix(fieldUtilities.field.getName());

            // adiciona o campo
            group.getLayouts().add(newPF);
        }

        // retorna o grupo
        return group;
    }

    private PanelField buildSelection(FieldUtilities fieldUtilities)
            throws BuilderException {

        SelectionPanelField group = new SelectionPanelField();

        String name = fieldUtilities.field.getDeclaringClass().getName() +
                "_selectionPanelGroup_" + fieldUtilities.field.getName() +
                "_" + fieldUtilities.alias;

        group.setName(name);
        group.setFieldClass(fieldUtilities.field.getType());
        group.setFieldName(fieldUtilities.field.getName());
        group.setEmptyForAll(false);
        group.setFilterType(fieldUtilities.selectionFilterType);

        // para cada campo emcapsulado
        for (FieldUtilities fu : fieldUtilities.embedded) {

            // desabilita o campo se não for seleção.
            //if (!fu.selection)
            //    unable(fu);

            // cria novos campos
            PanelField newPF = buildPanelField(fu);
            newPF.setOwner(group);

            // desabilita o campo se não for seleção.
            newPF.setEnabled(false);

            //if(newPF instanceof Criterionable)
            //    ((Criterionable)newPF).addPrefix(fieldUtilities.field.getName());
            
            // adiciona o campo
            group.getLayouts().add(newPF);
        }

        // cria e atribui o selection action
        SelectionAction sa = new SelectionAction();
        sa.setNotFoundMessage(fieldUtilities.notFoundMessage);
        sa.setSearchAlias(fieldUtilities.search);
        sa.setSearchClass(fieldUtilities.field.getType());
        sa.createAction();
        
        name = fieldUtilities.field.getDeclaringClass().getName() +
                "_selectionAction_" + fieldUtilities.field.getName() +
                "_" + fieldUtilities.alias;
        sa.setName(name);
        sa.setOwner(group);

        group.setSelectionAction(sa);

        // retorna o grupo
        return group;
    }

    private void unable(FieldUtilities fu){

        fu.properties.put("enabled", "False");
        
        if (fu.embedded != null)
            for (FieldUtilities fuEmb : fu.embedded) {
                unable(fuEmb);
            }
    }
}
